package rollercoaster.railway.tests;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.swing.JFrame;

import rollercoaster.commons.Vertex3D;
import rollercoaster.commons.interfaces.ICurve3D;
import rollercoaster.commons.interfaces.Surface3D;
import rollercoaster.railway.Circle;
import rollercoaster.railway.RoundedRectangle;
import rollercoaster.railway.Segment;
import rollercoaster.railway.Trajectory;
import rollercoaster.railway.Tube;
import rollercoaster.railway.UniformSecondOrderClosedBSpline;
import rollercoaster.railway.tests.Curve3DTest.GraphicListener;
import rollercoaster.railway.utils.OBJFileVertexReader;
import rollercoaster.railway.utils.VertexUtils;

import com.jogamp.opengl.util.FPSAnimator;

public class Surface3DTestTUBBBBE extends JFrame{
	
	public static void main(String[] args) {
		Surface3DTestTUBBBBE frame=new Surface3DTestTUBBBBE();
		frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
	
	public Surface3DTestTUBBBBE() {
		setSize(1200,1200);
		setTitle("Beautiful Carousel");
		GraphicListener listener=new GraphicListener();
		GLCanvas canvas=new GLCanvas();
		canvas.addGLEventListener(listener);
		getContentPane().add(canvas);
		canvas.addKeyListener(listener);
		addKeyListener(listener);
		FPSAnimator animator=new FPSAnimator(canvas,60);
		animator.start();
	}
	
	
	public static class GraphicListener implements GLEventListener,KeyListener{

		private static final int DIVISIONS_U=20;
		private static final int DIVISIONS_V=100;
		private static final int PALETTO_DIVISIONS_U=20;
		private static final int PALETTO_DIVISIONS_V=4;
		
		private int view_angle=-30;
		private int t=0;
		private float wx=1,hx=1;
		
		private Tube surf;
		private Tube surf2;
		private Tube surf3;
		
		private List<Tube> tubicini=new ArrayList<Tube>();
		
		private boolean terronamente=false;
		
		public void display(GLAutoDrawable arg0) {
			GL2 gl=(GL2) arg0.getGL();
			gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
			
			
			gl.glEnable(GL.GL_DEPTH_TEST);
			//gl.glCullFace(gl.GL_FRONT);
			//gl.glEnable(gl.GL_CULL_FACE);
			//gl.glFrontFace(gl.GL_CCW);
			gl.glDisable(gl.GL_CULL_FACE);
			
			gl.glMatrixMode(GL2.GL_PROJECTION);
			gl.glLoadIdentity();
			gl.glRotatef(view_angle, 1, 0, 0);
			gl.glScalef(1200/wx, 1200/hx, 1);

			
			
			gl.glMatrixMode(GL2.GL_MODELVIEW);
			gl.glLoadIdentity();
			gl.glScalef(0.03f, 0.03f, 0.03f);
			
			if(terronamente){
				drawSurfTerronamente(surf,gl);
				drawSurfTerronamente(surf2,gl);
				drawSurfTerronamente(surf3,gl);
			} else {
				gl.glColor3f(1, 1, 1);
				drawSurf(gl,surf,DIVISIONS_U,DIVISIONS_V);
				gl.glColor3f(1, 0, 0);
				drawSurf(gl,surf2,DIVISIONS_U,DIVISIONS_V);
				gl.glColor3f(1, 1, 0);
				drawSurf(gl,surf3,DIVISIONS_U,DIVISIONS_V);
			}
			
			for (Tube tube : tubicini) {
				drawSurf(gl, tube,PALETTO_DIVISIONS_U,PALETTO_DIVISIONS_V);
			}
			
			
			t++;
		}

		public void drawSurfTerronamente(Tube surf,GL2 gl){
			
			//Cerchietti attorno a cose a caso
			float stepu=surf.getEndUPoint()/DIVISIONS_U; 
			float stepv=surf.getEndVPoint()/DIVISIONS_V;
			gl.glLineWidth(2);
			gl.glShadeModel(GL2.GL_FLAT);
			
			for (int i = 0; i < DIVISIONS_V; i++) {
				
				float v=i*stepv;
				gl.glPointSize(1);
				gl.glColor3f(1, 1, 1);
				gl.glBegin(GL.GL_POINTS);
					Vertex3D vv=surf.getTrajectory().getBase().gamma(v);
					//System.out.println("Center:  "+vv);
					gl.glVertex3f(vv.getX(), vv.getY(), vv.getZ());
				gl.glEnd();
				
				//cerchietto
				if((int)i%2==0){
					gl.glColor3f(1, 1, 0);
				} else {
					gl.glColor3f(1, 0, 0);
				}
				
				
				gl.glBegin(GL.GL_LINE_STRIP);
				for (int j = 0; j <= DIVISIONS_U; j++) {
					float u=j*stepu;
					Vertex3D vertex=surf.sigma(u, v);
					if(t==0){
						//System.out.println(vertex);
					}
					gl.glVertex3f(vertex.getX(), vertex.getY(), vertex.getZ());
				}
				gl.glEnd();
			}
			
		}
		
		public void drawSurf(GL2 gl,Surface3D surf, int division_u, int division_v){
			
			float stepu=surf.getEndUPoint()/division_u;
			float stepv=surf.getEndVPoint()/division_v;
			//gl.glShadeModel(GL2.GL_FLAT);
			
			for(int i=0;i<division_v;i++){
				float v=stepv*i;
				float v1=v+stepv;
				gl.glBegin(GL.GL_TRIANGLE_STRIP);
				
					for(int j=0;j<=division_u;j++){
						float u=stepu*j;
						
						Vertex3D xyz=surf.sigma(u,v);
						float x=xyz.getX();
						float y=xyz.getY();
						float z=xyz.getZ();
					
						
						Vertex3D n=surf.getNormal(u, v);
						float nx=n.getX();
						float ny=n.getY();
						float nz=n.getZ();
						
				
						//Normale e vertice (u,v)
						gl.glNormal3f(nx, ny, nz);
						gl.glVertex3f(x,y,z);
						
						xyz=surf.sigma(u,v1);
						x=xyz.getX();
						y=xyz.getY();
						z=xyz.getZ();
						
						n=surf.getNormal(u, v1);
						nx=n.getX();
						ny=n.getY();
						nz=n.getZ();
			
						//Normale e vertice (u,v)
						gl.glNormal3f(nx, ny, nz);
						gl.glVertex3f(x,y,z);
						
					
						
					}
				gl.glEnd();
			}
		}
		
		public void drawCurve(ICurve3D curve, GL2 gl){
			gl.glLineWidth(2);
			gl.glShadeModel(GL2.GL_FLAT);
			gl.glColor3f(1, 1, 0);
			gl.glBegin(GL.GL_LINE_STRIP);
				float x=0f;
				for (int i = 0; i <= 100*(int)curve.getEndPoint(); i+=1) {
					if((int)i%2==0){
						gl.glColor3f(1, 1, 0);
					} else {
						gl.glColor3f(1, 0, 0);
					}
					Vertex3D v=curve.gamma(x);
					gl.glVertex3f(v.getX(), v.getY(), v.getZ());
					if(t==0){
						//System.out.println(v.getX()+" "+ v.getY()+" "+ v.getZ());
					}
					x+=0.01f;
				}
				
			gl.glEnd();
		}

		public void dispose(GLAutoDrawable arg0) {
			// TODO Auto-generated method stub
			
		}

	
		public void init(GLAutoDrawable arg0) {
			GL2 gl=(GL2) arg0.getGL();
			gl.glEnable(GL2.GL_LIGHTING);
			
			
			float ambient[]={0.5f,0.5f,0.5f,1};
			gl.glLightModelfv(GL2.GL_LIGHT_MODEL_AMBIENT, ambient, 0);
			
			gl.glEnable(GL2.GL_LIGHT4);
			float position4[]={0,1,0,1};
			gl.glLightfv(GL2.GL_LIGHT4, gl.GL_POSITION, position4,0);
			float intensity4[]={1,1,1,1};
			gl.glLightfv(GL2.GL_LIGHT4, gl.GL_DIFFUSE, intensity4,0);
			gl.glLightf(GL2.GL_LIGHT4,GL2.GL_LINEAR_ATTENUATION,0.5f);
			
			
			gl.glEnable(GL2.GL_COLOR_MATERIAL);
			gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE);
			float specColor[]={1,1,1,1};
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2.GL_SPECULAR, specColor, 0);
			gl.glMaterialf(GL.GL_FRONT_AND_BACK, GL2.GL_SHININESS, 80);
			
			/*
			OBJFileVertexReader vr=new OBJFileVertexReader("obbijey/cacca.obj");
			List<Vertex3D> vvs=vr.getVertices();
			drawCurve(new UniformSecondOrderClosedBSpline(vvs), gl);
			 */ 
			
			//Instance of the surf
			OBJFileVertexReader vr=new OBJFileVertexReader();
			UniformSecondOrderClosedBSpline base=new UniformSecondOrderClosedBSpline(vr.getVertices("obbijey/base_complex6.objxxx"));
			UniformSecondOrderClosedBSpline up=new UniformSecondOrderClosedBSpline(vr.getVertices("obbijey/up_complex6.objxxx"));
			
			Trajectory tr=new Trajectory(base, up);
			surf=new Tube(tr, new RoundedRectangle(1f,1f, 0.1f), new Vertex3D(0, 0, 0));
			//surf=new Tube(tr,new Circle(0.4f),new Vertex3D(0, 0, 0));
			
			double angle=Math.PI/4;
			float module=1.2f;
			float x=module*(float)Math.cos(angle);
			float y=module*(float)Math.sin(angle);
			surf2=new Tube(tr,new Circle(0.2f, new Vertex3D(0, 0, 0)), new Vertex3D(-x, -y, 0));
			surf3=new Tube(tr, new Circle(0.2f, new Vertex3D(0, 0, 0)), new Vertex3D(x, -y, 0));
			
			int numpaletti=100;
			float step=(surf.getEndVPoint()-surf.getStartVPoint())/numpaletti;
			for (int i = 0; i < numpaletti; i++) {
				float v=i*step;
				float u=(float)angle/(2f*(float)Math.PI);
				Vertex3D p0=surf.getTrajectory().getBase().gamma(v);
				Vertex3D p1=surf.sigma(u, v);
				Vertex3D p2=surf2.sigma(u+0.5f, v);
				Vertex3D p3=surf2.sigma(u, v);
				Vertex3D start=VertexUtils.meanPoint(p0, p1);
				Vertex3D end=VertexUtils.meanPoint(p2, p3);
				Vertex3D start2=surf.sigma(0.25f, v);
				Vertex3D end2=new Vertex3D(start.getX()-end.getX()+start2.getX(), start.getY()-end.getY()+start2.getY(), start.getY()-end.getY()+start2.getY());
				Trajectory seg=new Trajectory(new Segment(start,end),new Segment(start2, end2));
				Tube primopaletto=new Tube(seg, new Circle(0.1f, new Vertex3D(0, 0, 0)), new Vertex3D(0, 0, 0));
				tubicini.add(primopaletto);
				
				p1=surf.sigma(0.5f-u, v);
				p2=surf3.sigma(1-u, v);
				p3=surf3.sigma(0.5f-u, v);
				start=VertexUtils.meanPoint(p0, p1);
				end=VertexUtils.meanPoint(p2, p3);
				start2=surf.sigma(0.25f, v);
				end2=new Vertex3D(start.getX()-end.getX()+start2.getX(), start.getY()-end.getY()+start2.getY(), start.getY()-end.getY()+start2.getY());
				Trajectory seg2=new Trajectory(new Segment(start,end),new Segment(start2, end2));
				Tube secondopaletto=new Tube(seg2, new Circle(0.1f, new Vertex3D(0, 0, 0)), new Vertex3D(0, 0, 0));
				tubicini.add(secondopaletto);			
			}
			
		}
		public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3,
				int arg4) {
			GL gl=arg0.getGL();
			wx=arg3;
			hx=arg4;
			
		}

		public void keyTyped(KeyEvent e) {
			// TODO Auto-generated method stub
			
		}


		public void keyPressed(KeyEvent e) {
			if(e.getKeyCode()==KeyEvent.VK_UP){
				view_angle+=5;
			}
			if(e.getKeyCode()==KeyEvent.VK_DOWN){
				view_angle-=5;
			}
			
			if(e.getKeyCode()==KeyEvent.VK_T){
				terronamente=true;
			}
			if(e.getKeyCode()==KeyEvent.VK_P){
				terronamente=false;
			}
			
		}

		
		public void keyReleased(KeyEvent e) {
			// TODO Auto-generated method stub
			
		}
		
	}
}
